#include "stdafx.h"
#include "dot_scene.h"

namespace rpg {

	DotScene::DotScene(Ogre::SceneManager *sceneManager) : mSceneManager(sceneManager) {
	}

	DotScene::~DotScene(void) {
	}

	void DotScene::load(const Ogre::String &sceneName, const Ogre::String &groupName, Ogre::SceneNode *parentNode, const Ogre::String &nodeNamePrefix) {
		TiXmlDocument xmlDocument;
		const TiXmlElement *xmlRoot;
		const TiXmlElement *xmlNodes;

		mNodeNamePrefix = nodeNamePrefix;

		try {
			Ogre::DataStreamPtr dataStream = Ogre::ResourceGroupManager::getSingleton().openResource(sceneName, groupName);

			Ogre::String data = dataStream->getAsString();

			xmlDocument.Parse(data.c_str());
			dataStream->close();
			dataStream.setNull();

			if(xmlDocument.Error()) {
				//We'll just log, and continue on gracefully
				Ogre::LogManager::getSingleton().logMessage("[dotSceneLoader] The TiXmlDocument reported an error");
			}

			// Validate the File
			xmlRoot = xmlDocument.RootElement();
			if(Ogre::String(xmlRoot->Value()) != "scene") {
				Ogre::LogManager::getSingleton().logMessage( "[dotSceneLoader]Error: Invalid .scene File. Missing <scene>" );
				throw std::exception("Invalid .scene file");
			}

			// figure out where to attach any nodes we create
			if(parentNode == NULL) {
				parentNode = mSceneManager->getRootSceneNode();
			}

			xmlNodes = xmlRoot->FirstChildElement("nodes");

			// Read in the scene nodes
			if(xmlNodes != NULL) {
				parseNode(xmlNodes->FirstChildElement("node"), parentNode);
			}
		} catch(std::exception &) {
			//We'll just log, and continue on gracefully
			Ogre::LogManager::getSingleton().logMessage("[dotSceneLoader] Error loading scene");
		}
	}

	void DotScene::parseNode(const TiXmlElement *xmlNode, Ogre::SceneNode *parent) {
		const TiXmlElement *xmlPosition;
		const TiXmlElement *xmlRotation;
		const TiXmlElement *xmlScale;
		const TiXmlElement *xmlEntity;
		const TiXmlElement *xmlSubEntity;
		const TiXmlElement *xmlBillboardSet;
		const TiXmlElement *xmlLight;
		const TiXmlElement *xmlUserData;

		while(xmlNode != NULL) {
			// Process the current node
			// Grab the name of the node
			Ogre::String nodeName = xmlNode->Attribute("name");
			// First create the new scene node
			Ogre::SceneNode* newNode = parent->createChildSceneNode(mNodeNamePrefix + nodeName);
			Ogre::Vector3 tempVec;
			Ogre::String tempValue;

			// Now position it...
			xmlPosition = xmlNode->FirstChildElement("position");

			if(xmlPosition != NULL) {
				tempValue = xmlPosition->Attribute("x");
				tempVec.x = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlPosition->Attribute("y");
				tempVec.y = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlPosition->Attribute("z");
				tempVec.z = Ogre::StringConverter::parseReal(tempValue);
				newNode->setPosition(tempVec);
			}

			// Rotate it...
			xmlRotation = xmlNode->FirstChildElement("rotation");
			if(xmlRotation != NULL) {
				Ogre::Quaternion tempQuat;
				tempValue = xmlRotation->Attribute("qx");
				tempQuat.x = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlRotation->Attribute("qy");
				tempQuat.y = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlRotation->Attribute("qz");
				tempQuat.z = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlRotation->Attribute("qw");
				tempQuat.w = Ogre::StringConverter::parseReal(tempValue);
				newNode->setOrientation(tempQuat);
			}

			// Scale it.
			xmlScale = xmlNode->FirstChildElement("scale");
			if(xmlScale != NULL) {
				tempValue = xmlScale->Attribute("x");
				tempVec.x = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlScale->Attribute("y");
				tempVec.y = Ogre::StringConverter::parseReal(tempValue);
				tempValue = xmlScale->Attribute("z");
				tempVec.z = Ogre::StringConverter::parseReal(tempValue);
				newNode->setScale(tempVec);
			}

			xmlLight = xmlNode->FirstChildElement("light");
			if(xmlLight != NULL)
				newNode->attachObject(parseLight(xmlLight));

			// Check for an Entity
			xmlEntity = xmlNode->FirstChildElement("entity");
			if(xmlEntity != NULL)
			{
				Ogre::String entityName, entityMeshFilename;
				entityName = xmlEntity->Attribute("name");
				entityMeshFilename = xmlEntity->Attribute("meshFile");

				// Create entity
				Ogre::Entity *newEntity = mSceneManager->createEntity(entityName, entityMeshFilename);
	                        
				//castShadows      (true | false) "true"
				newEntity->setCastShadows(true);
				if(xmlEntity->Attribute("castShadows")) {
					if(Ogre::String(xmlEntity->Attribute("castShadows")) == "false") {
						newEntity->setCastShadows(false);
					}
				}

				// Go though sub entities
				xmlSubEntity = xmlEntity->FirstChildElement("subentities");

				if(xmlSubEntity != NULL) {
					xmlSubEntity = xmlSubEntity->FirstChildElement("subentity");

					while(xmlSubEntity != NULL) {
						Ogre::SubEntity *subEntity;
						CEGUI::uint subEntityIndex;

						subEntityIndex = Ogre::StringConverter::parseInt(xmlSubEntity->Attribute("index"));
						subEntity = newEntity->getSubEntity(subEntityIndex);
									
						if(xmlSubEntity->Attribute("materialName")) {
							subEntity->setMaterialName(xmlSubEntity->Attribute("materialName"));
						}

						xmlSubEntity= xmlSubEntity->NextSiblingElement("subentity");
					}
				}

	                   
				newNode->attachObject(newEntity);
			}

			xmlBillboardSet = xmlNode->FirstChildElement("billboardSet");
			if(xmlBillboardSet != NULL)
			{
				Ogre::String tempValue;

				Ogre::BillboardSet* billboardSet = mSceneManager->createBillboardSet(newNode->getName());

				Ogre::BillboardType billboardType;
				tempValue = xmlBillboardSet->Attribute("type");
				if(tempValue == "orientedCommon")
					billboardType = Ogre::BBT_ORIENTED_COMMON;
				else if(tempValue == "orientedSelf")
					billboardType = Ogre::BBT_ORIENTED_SELF;
				else billboardType = Ogre::BBT_POINT;

				Ogre::BillboardOrigin billboardOrigin;
				tempValue = xmlBillboardSet->Attribute("type");
				if(tempValue == "bottom_left")
					billboardOrigin = Ogre::BBO_BOTTOM_LEFT;
				else if(tempValue == "bottom_center")
					billboardOrigin = Ogre::BBO_BOTTOM_CENTER;
				else if(tempValue == "bottomRight")
					billboardOrigin = Ogre::BBO_BOTTOM_RIGHT;
				else if(tempValue == "left")
					billboardOrigin = Ogre::BBO_CENTER_LEFT;
				else if(tempValue == "right")
					billboardOrigin = Ogre::BBO_CENTER_RIGHT;
				else if(tempValue == "topLeft")
					billboardOrigin = Ogre::BBO_TOP_LEFT;
				else if(tempValue == "topCenter")
					billboardOrigin = Ogre::BBO_TOP_CENTER;
				else if(tempValue == "topRight")
					billboardOrigin = Ogre::BBO_TOP_RIGHT;
				else
					billboardOrigin = Ogre::BBO_CENTER;

				billboardSet->setBillboardType(billboardType);
				billboardSet->setBillboardOrigin(billboardOrigin);


				tempValue = xmlBillboardSet->Attribute("name");
				billboardSet->setMaterialName(tempValue);

				int width, height;
				width = (int)Ogre::StringConverter::parseReal(xmlBillboardSet->Attribute("width"));
				height = (int)Ogre::StringConverter::parseReal(xmlBillboardSet->Attribute("height"));
				billboardSet->setDefaultDimensions(width, height);
				billboardSet->setVisible(true);
				newNode->attachObject(billboardSet);

				const TiXmlElement *xmlBillboard;

				xmlBillboard = xmlBillboardSet->FirstChildElement("billboard");

				while(xmlBillboard != NULL) {
					Ogre::Billboard *billboard;

					tempVec = Ogre::Vector3::ZERO;
					Ogre::ColourValue tempColour(Ogre::ColourValue::White);

					xmlPosition = xmlBillboard->FirstChildElement("position");
					if(xmlPosition != NULL) {
						tempValue = xmlPosition->Attribute("x");
						tempVec.x = Ogre::StringConverter::parseReal(tempValue);
						tempValue = xmlPosition->Attribute("y");
						tempVec.y = Ogre::StringConverter::parseReal(tempValue);
						tempValue = xmlPosition->Attribute("z");
						tempVec.z = Ogre::StringConverter::parseReal(tempValue);
					}

					const TiXmlElement* xmlColour = xmlBillboard->FirstChildElement("colourDiffuse");
					if(xmlColour != NULL){
						tempValue = xmlColour->Attribute("r");
						tempColour.r = Ogre::StringConverter::parseReal(tempValue);
						tempValue = xmlColour->Attribute("g");
						tempColour.g = Ogre::StringConverter::parseReal(tempValue);
						tempValue = xmlColour->Attribute("b");
						tempColour.b = Ogre::StringConverter::parseReal(tempValue);
					}

					billboard = billboardSet->createBillboard(tempVec, tempColour);

					xmlBillboard = xmlBillboard->NextSiblingElement("billboard");
				}
			}

			xmlUserData = xmlNode->FirstChildElement("userData");
			if ( xmlUserData )
			{
			/*	const TiXmlElement *xmlProperty;
				XMLProperty = XMLUserData->FirstChildElement("property");
				while ( XMLProperty )
				{
					String first = NewNode->getName();
					String second = XMLProperty->Attribute("name");
					String third = XMLProperty->Attribute("data");
					String type = XMLProperty->Attribute("type");
					nodeProperty newProp(first,second,third,type);
					nodeProperties.push_back(newProp);
					XMLProperty = XMLProperty->NextSiblingElement("property");
				}   */
			}
	//-->		/*
			/*		Added by @lpha_Max ! :-)
			*		"Global" vars : 
			*			NewNode --> Node where attach the object...
			*			mSceneMgr --> SceneManager
			*/
			const TiXmlElement* xmlCamera = xmlNode->FirstChildElement("camera");
			if(xmlCamera != NULL) {
				Ogre::String cameraName = xmlCamera->Attribute("name");
				Ogre::Camera *newCamera = mSceneManager->createCamera(cameraName);
				newNode->attachObject(newCamera);
			}

			const TiXmlElement* xmlParticle = xmlNode->FirstChildElement("particleSystem");
			if (xmlParticle != NULL) {
				Ogre::String particleName = xmlParticle->Attribute("name");
				Ogre::String particleFile = xmlParticle->Attribute("file");
				Ogre::ParticleSystem *particleSystem = mSceneManager->createParticleSystem(particleName, particleFile);
				newNode->attachObject(particleSystem);
			}
	//<--
			const TiXmlElement * childXMLNode;
			childXMLNode = xmlNode->FirstChildElement("node");
			if(childXMLNode != NULL)
				parseNode(childXMLNode, newNode);	// recurse to do all my children

			xmlNode = xmlNode->NextSiblingElement("node"); // process my next sibling
		}
	}

	Ogre::Light * DotScene::parseLight(const TiXmlElement *xmlLight) {
		const TiXmlElement *xmlDiffuse;
		const TiXmlElement *xmlSpecular;
		const TiXmlElement *xmlAttentuation;
		const TiXmlElement *xmlPosition;

	   // Create a light (point | directional | spot | radPoint)
		Ogre::Light* newLight = mSceneManager->createLight(xmlLight->Attribute("name"));

		if((xmlLight->Attribute("type") == NULL) || Ogre::String(xmlLight->Attribute("type")) == "point")
			newLight->setType(Ogre::Light::LT_POINT);
		else if(Ogre::String(xmlLight->Attribute("type")) == "directional")
			newLight->setType(Ogre::Light::LT_DIRECTIONAL);
		else if(Ogre::String(xmlLight->Attribute("type")) == "spot")
			newLight->setType(Ogre::Light::LT_SPOTLIGHT);
		else if(Ogre::String(xmlLight->Attribute("type")) == "radPoint")
			newLight->setType(Ogre::Light::LT_POINT);

		xmlDiffuse = xmlLight->FirstChildElement("colourDiffuse");
		if(xmlDiffuse != NULL) {
			Ogre::ColourValue diffuse;
			diffuse.r = Ogre::StringConverter::parseReal(xmlDiffuse->Attribute("r"));
			diffuse.g = Ogre::StringConverter::parseReal(xmlDiffuse->Attribute("g"));
			diffuse.b = Ogre::StringConverter::parseReal(xmlDiffuse->Attribute("b"));
			diffuse.a = 1;
			newLight->setDiffuseColour(diffuse);
		}

		xmlSpecular = xmlLight->FirstChildElement("colourSpecular");
		if(xmlSpecular != NULL) {
			Ogre::ColourValue specular;
			specular.r = Ogre::StringConverter::parseReal(xmlSpecular->Attribute("r"));
			specular.g = Ogre::StringConverter::parseReal(xmlSpecular->Attribute("g"));
			specular.b = Ogre::StringConverter::parseReal(xmlSpecular->Attribute("b"));
			specular.a = 1;
			newLight->setSpecularColour(specular);
		}

		xmlAttentuation = xmlLight->FirstChildElement("lightAttenuation");
		if(xmlAttentuation != NULL) {
		  //get defaults incase not all values specified
			Ogre::Real range;
			Ogre::Real constant;
			Ogre::Real linear;
			Ogre::Real quadratic;

			range = newLight->getAttenuationRange();
			constant = newLight->getAttenuationConstant();
			linear = newLight->getAttenuationLinear();
			quadratic = newLight->getAttenuationQuadric();
	      
			if(xmlAttentuation->Attribute("range") != NULL)
				range = Ogre::StringConverter::parseReal(xmlAttentuation->Attribute("range"));
			if(xmlAttentuation->Attribute("constant") != NULL)
				constant = Ogre::StringConverter::parseReal(xmlAttentuation->Attribute("constant"));
			if(xmlAttentuation->Attribute("linear") != NULL)
				linear = Ogre::StringConverter::parseReal(xmlAttentuation->Attribute("linear"));
			if(xmlAttentuation->Attribute("quadratic") != NULL)
				quadratic = Ogre::StringConverter::parseReal(xmlAttentuation->Attribute("quadratic"));

			newLight->setAttenuation(range, constant, linear, quadratic);
		}

	   xmlPosition = xmlLight->FirstChildElement("position");
	   if(xmlPosition != NULL) {
		   Ogre::Vector3 position = Ogre::Vector3::ZERO;

			if(xmlPosition->Attribute("x") != NULL)
				position.x = Ogre::StringConverter::parseReal(xmlPosition->Attribute("x"));
			if(xmlPosition->Attribute("y") != NULL)
				position.y = Ogre::StringConverter::parseReal(xmlPosition->Attribute("y"));
			if(xmlPosition->Attribute("z") != NULL)
				position.z = Ogre::StringConverter::parseReal(xmlPosition->Attribute("z"));
	       
			newLight->setPosition(position);
		}

	   //castShadows      (true | false) "true"
		newLight->setCastShadows(true);
		if(xmlLight->Attribute("castShadows") != NULL) {
			if(Ogre::String(xmlLight->Attribute("castShadows")) == "false") {
				newLight->setCastShadows(false);
			}
		}
	      
	   //visible         (true | false) "true"      
		newLight->setVisible(true);
		if(xmlLight->Attribute("visible") != NULL) {
			if(Ogre::String(xmlLight->Attribute("visible")) == "false" ) {
				newLight->setVisible( false );
			}
		}

		return newLight;
	}

}